<?php declare(strict_types = 1);


namespace App\Model\Logic;

use App\Model\Entity\MixSecret;
use App\Model\Entity\OauthKeySecret;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\Annotation\Mapping\Inject;
use Swoft\Redis\Pool;

/**
 * @Bean()
 * app 令牌授权
 * Class OauthLogic
 * @package App\Model\Logic
 */
class OauthLogic
{

    /**
     * @Inject()
     * @var AesLogic
     */
    private $aes_logic;


    /**
     * @Inject("redis2.pool")
     * @var Pool
     */
    private $redis2;

    /**
     * @Inject()
     * @var Pool
     */
    private $redis;

    //令牌过期时间 秒
    const EXPIRE = 1800;
    /**
     * --------------------------------------------------------------------------
     * 生成 oauth appkey and app secret
     * --------------------------------------------------------------------------
     * @param $member_id
     * @param $serial_number
     * @return array|mixed
     */
    public function keySecret($member_id, $serial_number, $action)
    {
        $app_key = buildAppKey();
        if($action === 'update'){
            return $this->updateKeySecret($member_id, $serial_number);
        }
        $key = 'oauth:' . md5($app_key . $serial_number);
        $expire = self::EXPIRE;
        $oauth = $this->redis->get($key);
        if($oauth){
            $oauth = json_decode($oauth, true);
            return $oauth;
        } else{
            $option = ['member_id' => $member_id, 'serial_number' => $serial_number];
            $oauth = OauthKeySecret::where($option)->where('dead_time', '>=', time())->orderBy('created_at','desc')->first();
            if(!$oauth){
                $data = [
                    'app_key' => $app_key,
                    'app_secret'=> buildAppSecret(),
                    'created_at' => time(),
                    'dead_time' => time()+$expire,
                    'member_id' => $member_id,
                    'serial_number' => $serial_number,
                ];
                OauthKeySecret::insert($data);
                $save_data =  [
                    'app_key' => $data['app_key'],
                    'app_secret' => $data['app_secret'],
                    'member_id' => $member_id,
                    'invalid' => $data['dead_time'],
                ];
                $this->redis->set($key, json_encode($save_data), $expire);
                return $save_data;
            } else{
                return [
                    'app_key' => $oauth->app_key,
                    'app_secret' => $oauth->app_secret,
                    'member_id' => $member_id,
                    'invalid' => $oauth->dead_time,
                ];
            }
        }
    }


    /**
     * --------------------------------------------------------------------------
     * 更新oauth key secret
     * --------------------------------------------------------------------------
     * @param $member_id
     * @param $serial_number
     * @return array
     */
    public function updateKeySecret($member_id, $serial_number)
    {
        $app_key = buildAppKey();
        $key = 'oauth:' . md5($app_key . $serial_number);
        $expire = self::EXPIRE;
        $option = ['member_id' => $member_id, 'serial_number' => $serial_number];
        OauthKeySecret::where($option)->where('dead_time', '>=', time())->delete();
        $data = [
            'app_key' => $app_key,
            'app_secret'=> buildAppSecret(),
            'created_at' => time(),
            'dead_time' => time()+$expire,
            'member_id' => $member_id,
            'serial_number' => $serial_number,
        ];
        OauthKeySecret::insert($data);
        $save_data =  [
            'app_key' => $data['app_key'],
            'app_secret' => $data['app_secret'],
            'member_id' => $member_id,
            'invalid' => $data['dead_time'],
            'expire' => $expire
        ];
        $this->redis->set($key, json_encode($save_data), $expire);
        return $save_data;
    }

    /**
     * --------------------------------------------------------------------------
     * 验证oauth是否有效
     * --------------------------------------------------------------------------
     * @param $app_key
     * @param $serial_number
     * @return mixed|null
     */
    public function verifyOauth($app_key, $serial_number)
    {
        $key = 'oauth:' . md5($app_key . $serial_number);
        $oauth = $this->redis->get($key);
        if(!$oauth) return null;
        //验证成功后 继续延长用户的key 的时间
        $this->redis->set($key, json_encode(json_decode($oauth, true)), self::EXPIRE);
        $oauth = json_decode($oauth, true);
        return $oauth;
    }



    /**
     * 生成tik
     * @param $member_id
     * @return string
     */
    public function mkTik($member_id, $request = null, $is_valid = true)
    {
        if(empty($member_id) || 'null' == $member_id)
        {
            return "";
        }

        //由原来的每秒变化 修改为 每1小时变化一次
        $current_timestamp = strtotime(date('YmdH')."0000"); //time();
        $info_arr = [
            "mid" => $member_id,
            "sn" => $request->serial_number,
            "t" => $current_timestamp
        ];

        $json_str = json_encode($info_arr);
        $aes_key = config("app.aes_key");
        $tik =  $this->aes_logic->AESEncryptRequest($aes_key, $json_str);

        //是否开启后续验证tik
        if($is_valid)
        {
            $member_id_serial_number_key = $this->_getAuthMemberIdSerialNumberTikKey($member_id, $request->serial_number);
            $this->redis2->set($member_id_serial_number_key, $current_timestamp);
        }

        return base64_encode($tik);
    }


    private function _getAuthMemberIdSerialNumberTikKey($member_id, $serial_number)
    {
        return "tik:auth_" . $member_id . "_" . $serial_number;
    }



    /**
     * --------------------------------------------------------------------------
     * 设置单个设备与用户的混淆密钥
     * --------------------------------------------------------------------------
     * @param $serial_number
     * @param string $uuid
     * @return string
     */
    public function setMixSecret($serial_number, $uuid = '')
    {
        if(!$serial_number && !$uuid) return "";
        $key = self::mixSecretKey($serial_number, $uuid);
        $aes_key = config("app.aes_key");
        $mix = $this->aes_logic->AESEncryptRequest($aes_key, UUID() . $key);
        $data = [
            'uuid' => $uuid,
            'serial_number' => $serial_number,
            'mix_secret' => $mix,
            'created_at' => time(),
            'updated_at' => time()

        ];
        MixSecret::where('serial_number', $serial_number)->delete();
        MixSecret::insert($data);
        $this->redis->set($key, $mix);
        return $mix;
    }

    /**
     * --------------------------------------------------------------------------
     * 获取当前设备的混淆密钥 key
     * --------------------------------------------------------------------------
     * @param $serial_number
     * @param string $uuid
     * @return string
     */
    public function mixSecretKey($serial_number, $uuid = '')
    {
        if(!$serial_number && !$uuid) return "";
        $key = $serial_number . ':' . $uuid;
        return 'mixSecretKey:' . sha1($key);
    }

    /**
     * --------------------------------------------------------------------------
     * 获取当前设备的混淆密钥
     * --------------------------------------------------------------------------
     * @param $serial_number
     * @param string $uuid
     * @return mixed
     */
    public function getMixSecret($serial_number, $uuid = '')
    {
        $key = self::mixSecretKey($serial_number, $uuid);
        $mix = $this->redis->get($key);
        if(!$mix) return "";
        return $mix;
    }

}